import os
import sys
import imp
import md5

import utils


class TossfileManager(utils.Singleton):
    
    main_tossfile_absolute_path = property(lambda self: self.__main_tossfile_absolute_path)
    
    def __init__(self):
        self.__tossfile_modules = {}
        self.__pending_tossfile_md5_identifiers = set()
        self.__main_tossfile_md5_identifier = None
        self.__main_tossfile_absolute_path = None
    
    def is_main_tossfile_module(self, md5_identifier):
        return (self.__main_tossfile_md5_identifier != None and md5_identifier == self.__main_tossfile_md5_identifier)
    
    def get_tossfile_module(self, abspath):
        
        # use the md5 hash of the path to identify this module
        md5_identifier = md5.new(abspath).hexdigest()
        
        # track the root tossfile module
        if not self.__main_tossfile_md5_identifier:
            self.__main_tossfile_md5_identifier = md5_identifier
            self.__main_tossfile_absolute_path = abspath
            
        # make sure that load_source can find the sub-namespaces for tasks
        sys.path.append(abspath + "/" + "tossfile")
        
        if md5_identifier in self.__pending_tossfile_md5_identifiers:
            # we're still loading it, so just hold on right now
            return None

        if md5_identifier not in self.__tossfile_modules:

            if os.path.exists(abspath + "/tossfile.py"):
                filepath = abspath + "/tossfile.py"

            elif os.path.exists(abspath + "/tossfile/__init__.py"):
                filepath = abspath + "/tossfile/__init__.py"

            else:
                raise ImportError("no tossfile module found in '%s'" % abspath)

            try:
                self.__pending_tossfile_md5_identifiers.add(md5_identifier)
                self.__tossfile_modules[md5_identifier] = imp.load_source(md5_identifier, filepath)
                self.__pending_tossfile_md5_identifiers.remove(md5_identifier)
            except ImportError:
                raise RuntimeError("failed to import tossfile '%s' (%s)" % (filepath, sys.exc_info()[1]))
        
        return self.__tossfile_modules[md5_identifier]


class Tossfile(object):
    
    def __init__(self, path):
        
        # determine the absolute path of the tossfile
        if os.path.isabs(path):
            self.__absolute_path = path
        else:
            self.__absolute_path = os.path.abspath( "%s/%s" % (utils.absolute_directory_of_caller(), path))
        
        # keep track of the tossfile module we are bound to
        self.__tossfile_module = None
        self._try_to_bind_module()
    
    def _try_to_bind_module(self):
        self.__tossfile_module = TossfileManager.get_instance().get_tossfile_module(self.__absolute_path)
        return self.__tossfile_module
    
    def __getattr__(self, name):
        if not self.__tossfile_module:
            if not self._try_to_bind_module():
                raise RuntimeError("could not load tossfile in '%s'" % self.__absolute_path)
        return getattr(self.__tossfile_module, name)
